# -*- coding: utf-8 -*-
# created on 2016/10/14


from mathsolver.functions.base import *
from sympy import degree, sqrt, pi, Abs, collect, Poly, solveset
from sympy.abc import x, y
from mathsolver.functions.root.jiefangchen import JieFangChen
from mathsolver.functions.zhixian.base import default_symbols
from mathsolver.functions.yuan.base import peifang
from mathsolver.functions.zhixian.property import ZhiXianJudge
from mathsolver.functions.yuan.property import ErYuanErCiJudge, ErYuanErCiCoeff


# 椭圆类
class Oval(object):
    def __init__(self, eq):
        self.eq = eq.sympify()
        self.normalizedEq = self.normalized()
        self.A, self.B, self.C, self.eccentricity, self.xCoeff, self.yCoeff = self.coeff()
        self.SymmetryPoint = self.get_symmetry_point()
        self.longAxis = BaseValue(2 * self.A.sympify())
        self.shortAxis = BaseValue(2 * self.B.sympify())
        self.jiaoju = BaseValue(2 * self.C.sympify())
        self.peakPonits, self.focusPoints = self.get_points()
        self.directrix = self.get_directrix()
        self.SymmetryAxis = self.get_symmetry_axis()
        self.area = self.oval_area()

    def normalized(self):
        f1, f2 = self.eq
        symx, symy = default_symbols(f1 - f2)
        expr = f1 - f2
        symx_expr = S.Zero
        symy_expr = S.Zero
        constant_expr = S.Zero
        for arg in expr.args:
            arg_symbols = arg.free_symbols
            if (symx in arg_symbols) and (symy not in arg_symbols):
                symx_expr += arg
            if (symy in arg_symbols) and (symx not in arg_symbols):
                symy_expr += arg
            if (symx not in arg_symbols) and (symy not in arg_symbols):
                constant_expr += arg
        new_symx_expr = peifang(symx_expr, symx)
        ex = new_symx_expr
        cont1 = S.Zero
        if ex.is_Add:
            cont1, ex = ex.as_independent(symx)
        new_symy_expr = peifang(symy_expr, symy)
        ey = new_symy_expr
        cont2 = S.Zero
        if ey.is_Add:
            cont2, ey = ey.as_independent(symy)
        new_constant = -(cont1 + cont2 + constant_expr)
        return BaseEq([ex / new_constant + ey / new_constant, 1])

    def coeff(self):
        eq = self.normalizedEq.sympify()
        eq_left = eq[0]
        args_list = []
        for arg in eq_left.args:
            args_list.append(arg)
        x_coeff = S.Zero
        y_coeff = S.Zero
        if args_list[0].is_Mul:
            x_coeff = 1 / args_list[0].args[0]
        elif args_list[0].is_Pow:
            x_coeff = 1
        if args_list[1].is_Mul:
            y_coeff = 1 / args_list[1].args[0]
        elif args_list[1].is_Pow:
            y_coeff = 1

        a = max(sqrt(x_coeff), sqrt(y_coeff))
        b = min(sqrt(x_coeff), sqrt(y_coeff))
        c = sqrt(a ** 2 - b ** 2)
        e = c / a
        return BaseValue(a), BaseValue(b), BaseValue(c), BaseValue(e), \
            BaseValue(x_coeff), BaseValue(y_coeff)

    def get_symmetry_point(self):
        eq = self.normalizedEq.sympify()
        eq_left = eq[0]
        jies = []
        for arg in eq_left.args:
            jie = JieFangChen().solver(BaseEq([arg, S.Zero])).output[0].value
            jies.append(jie)
        point_x = jies[0][jies[0].keys()[0]].args[0]
        point_y = jies[1][jies[1].keys()[0]].args[0]
        return BasePoint({"name": "Symmetry", "value": [point_x, point_y]})

    def get_points(self):
        symmetry_point = self.SymmetryPoint.sympify()
        a = self.A.sympify()
        b = self.B.sympify()
        c = self.C.sympify()
        x_coeff = self.xCoeff.sympify()
        y_coeff = self.yCoeff.sympify()
        if x_coeff > y_coeff:
            peak_ponits = BasePoints([{"name": "left", "value": [symmetry_point[0] - a, symmetry_point[1]]},
                                     {"name": "right", "value": [symmetry_point[0] + a, symmetry_point[1]]},
                                     {"name": "up", "value": [symmetry_point[0], symmetry_point[1] + b]},
                                     {"name": "down", "value": [symmetry_point[0], symmetry_point[1] - b]}])
            focus_points = BasePoints([{"name": "left", "value": [symmetry_point[0] - c, symmetry_point[1]]},
                                      {"name": "right", "value": [symmetry_point[0] + c, symmetry_point[1]]}])
        else:
            peak_ponits = BasePoints([{"name": "left", "value": [symmetry_point[0] - b, symmetry_point[1]]},
                                     {"name": "right", "value": [symmetry_point[0] + b, symmetry_point[1]]},
                                     {"name": "up", "value": [symmetry_point[0], symmetry_point[1] + a]},
                                     {"name": "down", "value": [symmetry_point[0], symmetry_point[1] - a]}])
            focus_points = BasePoints([{"name": "up", "value": [symmetry_point[0], symmetry_point[1] + c]},
                                      {"name": "right", "value": [symmetry_point[0], symmetry_point[1] - c]}])
        return peak_ponits, focus_points

    def get_directrix(self):
        symmetry_point = self.SymmetryPoint.sympify()
        a = self.A.sympify()
        c = self.C.sympify()
        x_coeff = self.xCoeff.sympify()
        y_coeff = self.yCoeff.sympify()
        if x_coeff > y_coeff:
            directrix = BaseZhiXians([{"name": "left", "value": [x, symmetry_point[0] - a ** 2 / c]},
                                      {"name": "right", "value": [x, symmetry_point[0] + a ** 2 / c]}])
        else:
            directrix = BaseZhiXians([{"name": "up", "value": [y, symmetry_point[1] + a ** 2 / c]},
                                      {"name": "down", "value": [y, symmetry_point[1] - a ** 2 / c]}])
        return directrix

    def get_symmetry_axis(self):
        symmetry_point = self.SymmetryPoint.sympify()
        return BaseZhiXians([{"name": "", "value": [x, symmetry_point[0]]}, {"name": "", "value": [y, symmetry_point[1]]}])

    def oval_area(self):
        a = self.A.sympify()
        b = self.B.sympify()
        s = pi * a * b
        return BaseValue(s)


# 双曲线
class Hyperbola(object):
    def __init__(self, eq):
        self.eq = eq.sympify()
        self.normalizedEq = self.normalized()
        self.A, self.B, self.C, self.eccentricity, self.xCoeff, self.yCoeff = self.coeff()
        self.SymmetryPoint = self.get_symmetry_point()
        self.realAxis = BaseValue(2 * self.A.sympify())
        self.imaginaryAxis = BaseValue(2 * self.B.sympify())
        self.jiaoju = BaseValue(2 * self.C.sympify())
        self.peakPonits, self.focusPoints = self.get_points()
        self.directrix = self.get_directrix()
        self.SymmetryAxis = self.get_symmetry_axis()
        self.asymptote = self.get_asymptote()

    def normalized(self):
        f1, f2 = self.eq
        symx, symy = default_symbols(f1 - f2)
        expr = f1 - f2
        symx_expr = S.Zero
        symy_expr = S.Zero
        constant_expr = S.Zero
        for arg in expr.args:
            arg_symbols = arg.free_symbols
            if (symx in arg_symbols) and (symy not in arg_symbols):
                symx_expr += arg
            if (symy in arg_symbols) and (symx not in arg_symbols):
                symy_expr += arg
            if (symx not in arg_symbols) and (symy not in arg_symbols):
                constant_expr += arg
        new_symx_expr = peifang(symx_expr, symx)
        ex = new_symx_expr
        cont1 = S.Zero
        if ex.is_Add:
            cont1, ex = ex.as_independent(symx)
        new_symy_expr = peifang(symy_expr, symy)
        ey = new_symy_expr
        cont2 = S.Zero
        if ey.is_Add:
            cont2, ey = ey.as_independent(symy)
        new_constant = -(cont1 + cont2 + constant_expr)
        return BaseEq([ex / new_constant + ey / new_constant, 1])

    def coeff(self):
        eq = self.normalizedEq.sympify()
        eq_left = eq[0]
        args_list = []
        for arg in eq_left.args:
            args_list.append(arg)
        x_coeff = S.Zero
        y_coeff = S.Zero
        if args_list[0].is_Mul:
            x_coeff = 1 / args_list[0].args[0]
        elif args_list[0].is_Pow:
            x_coeff = 1
        if args_list[1].is_Mul:
            y_coeff = 1 / args_list[1].args[0]
        elif args_list[1].is_Pow:
            y_coeff = 1
        if x_coeff > 0:
            a = sqrt(x_coeff)
            b = sqrt(Abs(y_coeff))
        else:
            a = sqrt(y_coeff)
            b = sqrt(Abs(x_coeff))
        c = sqrt(a ** 2 + b ** 2)
        e = c / a
        return BaseValue(a), BaseValue(b), BaseValue(c), BaseValue(e), BaseValue(x_coeff), BaseValue(y_coeff)

    def get_symmetry_point(self):
        eq = self.normalizedEq.sympify()
        eq_left = eq[0]
        jies = []
        for arg in eq_left.args:
            jie = JieFangChen().solver(BaseEq([arg, S.Zero])).output[0].value
            jies.append(jie)
        point_x = jies[0][jies[0].keys()[0]].args[0]
        point_y = jies[1][jies[1].keys()[0]].args[0]
        return BasePoint({"name": "symmetrical", "value": [point_x, point_y]})

    def get_points(self):
        x_coeff = self.xCoeff.sympify()
        a = self.A.sympify()
        c = self.C.sympify()
        symmetrical_center = self.SymmetryPoint.sympify()
        if x_coeff > 0:
            peak_ponits = BasePoints([{"name": "left", "value": [symmetrical_center[0] - a, symmetrical_center[1]]},
                                     {"name": "right", "value": [symmetrical_center[0] + a, symmetrical_center[1]]}])
            focus_points = BasePoints([{"name": "left", "value": [symmetrical_center[0] - c, symmetrical_center[1]]},
                                      {"name": "right", "value": [symmetrical_center[0] + c, symmetrical_center[1]]}])
        else:
            peak_ponits = BasePoints([{"name": "up", "value": [symmetrical_center[0], symmetrical_center[1] + a]},
                                     {"name": "down", "value": [symmetrical_center[0], symmetrical_center[1] - a]}])
            focus_points = BasePoints([{"name": "up", "value": [symmetrical_center[0], symmetrical_center[1] + c]},
                                      {"name": "down", "value": [symmetrical_center[0], symmetrical_center[1] - c]}])
        return peak_ponits, focus_points

    def get_directrix(self):
        symmetrical_point = self.SymmetryPoint.sympify()
        a = self.A.sympify()
        c = self.C.sympify()
        x_coeff = self.xCoeff.sympify()
        if x_coeff > 0:
            directrix = BaseZhiXians([{"name": "left", "value": [x, symmetrical_point[0] - a ** 2 / c]},
                                      {"name": "right", "value": [x, symmetrical_point[0] + a ** 2 / c]}])
        else:
            directrix = BaseZhiXians([{"name": "up", "value": [y, symmetrical_point[1] + a ** 2 / c]},
                                      {"name": "down", "value": [y, symmetrical_point[1] - a ** 2 / c]}])
        return directrix

    def get_symmetry_axis(self):
        symmetrical_point = self.SymmetryPoint.sympify()
        symmetry_axis = BaseZhiXians([{"name": "x", "value": [x, symmetrical_point[0]]},
                                     {"name": "y", "value": [y, symmetrical_point[1]]}])
        return symmetry_axis

    def get_asymptote(self):
        a = self.A.sympify()
        b = self.B.sympify()
        x_coeff = self.xCoeff.sympify()
        if x_coeff > 0:
            asymptote = BaseZhiXians([{"name": "OneAndThree", "value": [y, b * x / a]},
                                      {"name": "TwoAndFo", "value": [y, - b * x / a]}])
        else:
            asymptote = BaseZhiXians([{"name": "OneAndThree", "value": [y, a * x / b]},
                                      {"name": "TwoAndFo", "value": [y, - a * x / b]}])
        return asymptote


# 抛物线
class Parabola(object):
    def __init__(self, eq):
        self.eq = eq.sympify()
        self.normalizedEq = self.normalized()
        self.peakPoint = self.get_peak_ponit()
        self.SymmetryAxis = self.get_symmetry_axis()
        self.focusPoint = self.get_focus_point()
        self.directrix = self.get_directrix()

    def normalized(self):
        f1, f2 = self.eq
        symx, symy = default_symbols(f1 - f2)
        expr = f1 - f2
        left = S.Zero
        right = S.Zero
        if degree(expr, symx) == 1:
            ex, ey = expr.as_independent(symy)
            new_ey = peifang(ey, symy)
            if new_ey.is_Mul:
                coeff, left = new_ey.as_independent(symy)
                right = - ex / coeff
            elif new_ey.is_Add:
                constant, left = new_ey.as_independent(symy)
                coeff = 1
                if left.is_Mul:
                    coeff = left.args[0]
                    left = left.args[1]
                right = - (constant + ex) / coeff
        else:
            ey, ex = expr.as_independent(symx)
            new_ex = peifang(ex, symx)
            if new_ex.is_Mul:
                coeff, left = new_ex.as_independent(symx)
                right = - ey / coeff
            elif new_ex.is_Add:
                constant, left = new_ex.as_independent(symx)
                coeff = 1
                if left.is_Mul:
                    coeff = left.args[0]
                    left = left.args[1]
                right = - (constant + ey) / coeff
        return BaseEq([left, right])

    def get_peak_ponit(self):
        eq = self.normalizedEq.sympify()
        symx, symy = default_symbols(eq[0] - eq[1])
        answer_left = JieFangChen().solver(BaseEq([eq[0], S.Zero])).output[0].value
        answer_right = JieFangChen().solver(BaseEq([eq[1], S.Zero])).output[0].value
        if answer_left.keys()[0] == symx:
            x_value = answer_left[answer_left.keys()[0]].args[0]
            y_value = answer_right[answer_right.keys()[0]].args[0]
        else:
            y_value = answer_left[answer_left.keys()[0]].args[0]
            x_value = answer_right[answer_right.keys()[0]].args[0]
        return BasePoint({"name": "", "value": [x_value, y_value]})

    def get_symmetry_axis(self):
        peak_point = self.peakPoint.sympify()
        eq = self.normalizedEq.sympify()
        symx, symy = default_symbols(eq[0] - eq[1])
        if eq[0].has(symx):
            return BaseZhiXian({"name": "", "value": [symx, peak_point[0]]})
        else:
            return BaseZhiXian({"name": "", "value": [symy, peak_point[1]]})

    def get_focus_point(self):
        peak_point = self.peakPoint.sympify()
        eq = self.normalizedEq.sympify()
        symx, symy = default_symbols(eq[0] - eq[1])
        if eq[0].has(symx):
            y_coeff = eq[1].coeff(symy)
            return BasePoint({"name": "", "value": [peak_point[0], peak_point[1] + y_coeff / 4]})
        else:
            x_coeff = eq[1].coeff(symx)
            return BasePoint({"name": "", "value": [peak_point[0] + x_coeff / 4, peak_point[1]]})

    def get_directrix(self):
        peak_point = self.peakPoint.sympify()
        eq = self.normalizedEq.sympify()
        symx, symy = default_symbols(eq[0] - eq[1])
        if eq[0].has(symx):
            y_coeff = eq[1].coeff(symy)
            return BaseZhiXian({"name": "", "value": [symy, peak_point[1] - y_coeff / 4]})
        else:
            x_coeff = eq[1].coeff(symx)
            return BasePoint({"name": "", "value": [symx, peak_point[0] - x_coeff / 4]})


# 两条曲线联立得到一元二次方程
class CurveAndCurveTogether(BaseFunction):
    @staticmethod
    def coeff_normalization(coeff):
        new_coeff = None
        if coeff[0] != 0:
            new_coeff = [coeff[0] / coeff[0], coeff[1] / coeff[0], coeff[2] / coeff[0],
                         coeff[3] / coeff[0], coeff[4] / coeff[0], coeff[5] / coeff[0]]
        elif coeff[0] == 0 and coeff[1] != 0:
            new_coeff = [coeff[0] / coeff[1], coeff[1] / coeff[1], coeff[2] / coeff[1],
                         coeff[3] / coeff[1], coeff[4] / coeff[1], coeff[5] / coeff[1]]
        elif coeff[0] == 0 and coeff[1] == 0 and coeff[2] != 0:
            new_coeff = [coeff[0] / coeff[2], coeff[1] / coeff[2], coeff[2] / coeff[2],
                         coeff[3] / coeff[2], coeff[4] / coeff[2], coeff[5] / coeff[2]]
        return new_coeff

    def solver(self, *args):
        eq1 = args[0].sympify()
        expr1 = (eq1[0] - eq1[1]).expand().simplify()
        symx, symy = default_symbols(expr1)
        eq2 = args[1].sympify()
        expr2 = (eq2[0] - eq2[1]).expand().simplify()
        answer1 = ZhiXianJudge().solver(args[0]).output[0].sympify()
        answer2 = ErYuanErCiJudge().solver(args[0]).output[0].sympify()
        answer3 = ErYuanErCiJudge().solver(args[1]).output[0].sympify()
        eqs = [eq1, eq2]
        self.steps.append(["依题意，得", BaseEqs(eqs).printing()])
        if answer1 and answer3:
            symx, symy = default_symbols(expr1)
            jies = solveset(expr1, symy)
            target_value = list(jies)[0]
            new_expr2 = expr2.subs({symy: target_value})
            new_expr2 = collect(new_expr2.expand().simplify(), symx)
            self.steps.append(["代入消元，得", BaseEq([new_expr2, S.Zero]).printing()])
            self.output.append(BaseEq([new_expr2, S.Zero]))
        elif answer2 and answer3:
            expr1_coeff = ErYuanErCiCoeff().solver(args[0]).output[0].sympify()
            expr2_coeff = ErYuanErCiCoeff().solver(args[1]).output[0].sympify()
            a1, b1, c1, d1, e1, f1 = self.coeff_normalization(expr1_coeff)
            a2, b2, c2, d2, e2, f2 = self.coeff_normalization(expr2_coeff)
            if a1 == a2 and b1 == b2 and c1 == c2:
                expr3 = (d1 - d2) * symx + (e1 - e2) * symy + (f1 - f2)
                jies = solveset(expr3, symy)
                target_value = list(jies)[0]
                new_expr1 = expr1.subs({symy: target_value})
                new_expr1 = collect(new_expr1.expand().simplify(), symx)
                self.steps.append(["代入消元，得", BaseEq([new_expr1, S.Zero]).printing()])
                self.output.append(BaseEq([new_expr1, S.Zero]))
        self.label.add("消元法化方程组为方程")
        return self


# 两根之和
class ErCiSumsOfRoots(BaseFunction):
    def solver(self, *args):
        f1, f2 = args[0].sympify()
        expr = (f1 - f2).expand().simplify()
        if expr.has(x) and not expr.has(y):
            symbol = x
        elif not expr.has(x) and expr.has(y):
            symbol = y
        poly = Poly(expr.as_expr(), symbol)
        a, b, c = poly.all_coeffs()
        add = - b / a
        self.steps.append(["", "两根和为: %s" % (new_latex(add))])
        self.output.append(BaseValue(add))
        self.label.add("两根之和")
        return self


# 两根之积
class ErCiMulOfRoots(BaseFunction):
    def solver(self, *args):
        f1, f2 = args[0].sympify()
        expr = (f1 - f2).expand().simplify()
        if expr.has(x) and not expr.has(y):
            symbol = x
        elif not expr.has(x) and expr.has(y):
            symbol = y
        poly = Poly(expr.as_expr(), symbol)
        a, b, c = poly.all_coeffs()
        mul = c / a
        self.steps.append(["", "两根之积为: %s" % (new_latex(mul))])
        self.output.append(BaseValue(mul))
        self.label.add("两根之积")
        return self


# 多方程或不等式构成方程组或不等式组
class GouChengEqsIneqs(BaseFunction):
    def solver(self, *args):
        num = len(args)
        eqs = []
        for i in range(0, num):
            eqs.append(args[i].sympify())
        new_ineqs = []
        for eq in eqs:
            if isinstance(eq, list):
                for arg in eq:
                    new_ineqs.append(arg)
            else:
                new_ineqs.append(eq)
        self.output.append(base_gen(new_ineqs))
        return self


if __name__ == '__main__':
    pass
